﻿#include <occmesh/frame/MainWindow.h>
#include <occmesh/frame/OCCWidget.h>

#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    this->setWindowTitle("MainWindow");

    // 逐层级启用鼠标追踪
    this->setMouseTracking(true);
    ui->centralWidget->setMouseTracking(true);

    // 初始化连接
    QObject::connect(ui->actionObject, SIGNAL(triggered(bool)), this, SLOT(openFile()));
    QObject::connect(ui->actionCloud, SIGNAL(triggered(bool)), this, SLOT(openCloud()));
    QObject::connect(ui->actionDir, SIGNAL(triggered(bool)), this, SLOT(openDirectory()));
    QObject::connect(ui->actionSaveObj, SIGNAL(triggered(bool)), this, SLOT(saveFile()));
    QObject::connect(ui->actionSaveCloud, SIGNAL(triggered(bool)), this, SLOT(saveCloud()));
    QObject::connect(ui->actionSpecific, SIGNAL(triggered(bool)), this, SLOT(saveSpecific()));
    QObject::connect(ui->actionDelete, SIGNAL(triggered(bool)), this, SLOT(deleteFile()));

    QObject::connect(ui->actionSmooth, SIGNAL(triggered(bool)), this, SLOT(smoothShade()));
    QObject::connect(ui->actionFlat, SIGNAL(triggered(bool)), this, SLOT(flatShade()));

    QObject::connect(ui->actionMeshFitting, SIGNAL(triggered(bool)), this, SLOT(meshFitting()));
    QObject::connect(ui->actionCovering, SIGNAL(triggered(bool)), this, SLOT(covering()));
    QObject::connect(ui->actionFilling, SIGNAL(triggered(bool)), this, SLOT(filling()));
    QObject::connect(ui->actionBlending, SIGNAL(triggered(bool)), this, SLOT(blending()));

    // 构造完成后调用
    QTimer::singleShot(0, this, &MainWindow::initializeChildren);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::AddPointCloud(const std::vector<gp_Pnt> &points)
{
    Handle(TColgp_HArray1OfPnt) cloud = new TColgp_HArray1OfPnt(1, points.size());
    for (int i = 1; i <= cloud->Size(); i++)
        cloud->SetValue(i, points[i - 1]);
    Handle(AIS_PointCloud) c(new AIS_PointCloud);
    c->SetPoints(cloud);
    m_clouds.push_back(c);
}

void MainWindow::AddCurve(Handle(Geom_Curve) curve, double first, double last)
{
    TopoDS_Shape edge = BRepBuilderAPI_MakeEdge(curve, first, last);
    this->AddShape(edge);
}

void MainWindow::AddSurface(Handle(Geom_Surface) surface)
{
    TopoDS_Shape face = BRepBuilderAPI_MakeFace(surface, 1e-16);
    this->AddShape(face);
}

void MainWindow::AddShape(const TopoDS_Shape &shape)
{
    // 推入形状
    m_shapes.push_back(shape);
}

void MainWindow::AddMesh(MeshPtr mesh)
{
    Mesh &meshRef = *mesh;

    // 获取顶点和面数
    int numVertices = meshRef.n_vertices();
    int numFaces = meshRef.n_faces();

    // 创建 OCC 三角网格对象，注意后面两个 bool 控制纹理和法向
    Handle(Poly_Triangulation) occTriangulation = new Poly_Triangulation(numVertices, numFaces, true, true);

    // 1. 转换顶点数据
    int vertexIndex = 1;
    for (Mesh::VertexIter v_it = meshRef.vertices_begin(); v_it != meshRef.vertices_end(); ++v_it, ++vertexIndex)
    {
        const Mesh::Point &p = meshRef.point(*v_it);
        occTriangulation->SetNode(vertexIndex, gp_Pnt(p[0], p[1], p[2]));

        if (mesh->has_face_normals())
        {
            auto normal = meshRef.normal(*v_it);
            // std::cout << "Normal: " << normal[0] << "," << normal[1] << "," << normal[2] << std::endl;
            if (normal.max_abs() < 1e-6)
                normal[2] = 1;
            occTriangulation->SetNormal(vertexIndex, gp_Dir(normal[0], normal[1], normal[2]));
        }
    }

    // 2. 转换三角形面数据
    Poly_Array1OfTriangle &triangles = occTriangulation->ChangeTriangles();
    int faceIndex = 1; // OCC 数组从 1 开始索引

    for (Mesh::FaceIter f_it = meshRef.faces_begin(); f_it != meshRef.faces_end(); ++f_it, ++faceIndex)
    {
        // 获取面的三个顶点索引
        Mesh::FaceVertexIter fv_it = meshRef.fv_iter(*f_it);
        int idx1 = fv_it->idx() + 1; // meshRef 索引从 0 开始，OCC 从 1 开始
        ++fv_it;
        int idx2 = fv_it->idx() + 1;
        ++fv_it;
        int idx3 = fv_it->idx() + 1;

        triangles.SetValue(faceIndex, Poly_Triangle(idx1, idx2, idx3));
    }

    m_meshes.insert(std::make_pair(occTriangulation, mesh));
}

void MainWindow::ReadSTP(const char *path)
{
    // 创建读取器
    STEPControl_Reader reader;
    reader.ReadFile(Standard_CString(path));

    // 检查读取信息
    reader.PrintCheckLoad(true, IFSelect_ItemsByEntity);

    // 获得可翻译的根实体数量，然后翻译所有实体，获得成功的数量
    int NbRoots = reader.NbRootsForTransfer();
    int NbTrans = reader.TransferRoots();

    // 检查翻译信息
    reader.PrintCheckTransfer(true, IFSelect_ItemsByEntity);

    // 添加获得的形状
    for (int i = 1; i <= reader.NbShapes(); i++)
    {
        auto shape = reader.Shape(i);
        this->AddShape(shape);
    }

    std::cout << "Read " << reader.NbShapes() << " shapes." << std::endl;
}

void MainWindow::WriteSTP(const char *path)
{
    // 创建写入器
    STEPControl_Writer writer;

    // 翻译所有形状
    for (auto sh : m_shapes)
        writer.Transfer(sh, STEPControl_AsIs);

    // 写入文件
    writer.Write(Standard_CString(path));
}

void MainWindow::ReadIGS(const char *path)
{
    // 读取器
    IGESControl_Reader reader;

    // 读取IGES文件
    int status = reader.ReadFile(path);
    int NbRoots = reader.NbRootsForTransfer();
    int NbTrans = reader.TransferRoots();

    // 添加获得的形状
    for (int i = 1; i <= reader.NbShapes(); i++)
    {
        auto shape = reader.Shape(i);
        this->AddShape(shape);
    }
}

void MainWindow::WriteIGS(const char *path)
{
    // 创建写入器
    IGESControl_Writer writer;

    // 翻译所有形状
    for (auto sh : m_shapes)
        writer.AddShape(sh);

    // 写入文件
    writer.Write(Standard_CString(path));
}

void MainWindow::ReadBRep(const char *path)
{
    // 创建空形状保存
    TopoDS_Shape shape;

    // 构建器
    BRep_Builder builder;
    if (BRepTools::Read(shape, path, builder))
        this->AddShape(shape);
    else
        QMessageBox::information(this, "Tip", "Failed.");
}

void MainWindow::WriteBRep(const char *path)
{
}

void MainWindow::ReadCloud(const char *path)
{
    // 创建空形状保存
    TopoDS_Shape shape;

    // 构建器
    BRep_Builder builder;
    if (BRepTools::Read(shape, path, builder))
    {
        if (shape.ShapeType() == TopAbs_COMPOUND)
        {
            TopExp_Explorer Ex;
            BRep_Tool tool;

            std::vector<gp_Pnt> points;
            for (Ex.Init(shape, TopAbs_VERTEX); Ex.More(); Ex.Next())
            {
                TopoDS_Vertex V;
                V = TopoDS::Vertex(Ex.Current());

                gp_Pnt P = tool.Pnt(V);
                points.push_back(P);
            }

            if (!points.empty())
                this->AddPointCloud(points);
        }
        else
            this->AddShape(shape);
    }
    else
        QMessageBox::information(this, "Tip", "Failed.");
}

void MainWindow::WriteCloud(const char *path)
{
    // 创建复合体
    TopoDS_Compound compound;
    BRep_Builder builder;
    builder.MakeCompound(compound);

    auto cloud = ui->occWidget->SelectedCloud();
    if (!cloud.IsNull())
    {
        auto points = cloud->GetPoints();

        // 将每个点转换为一个顶点并添加到复合体中
        for (int i = 1; i <= points->VertexNumber(); ++i)
        {
            double x, y, z;
            points->Vertice(i, x, y, z);
            TopoDS_Vertex vertex = BRepBuilderAPI_MakeVertex({x, y, z});
            builder.Add(compound, vertex);
        }

        // 将复合体导出为 BREP 文件
        BRepTools::Write(compound, path);
    }
}

void MainWindow::ReadMesh(const char *path)
{
    MeshPtr mesh = std::make_shared<Mesh>();
    Mesh &meshRef = *mesh;

    meshRef.request_face_normals();
    meshRef.request_vertex_normals();
    meshRef.request_vertex_texcoords2D();

    // 设置选项：确保读取时自动三角化
    OpenMesh::IO::Options opts;
    opts += OpenMesh::IO::Options::VertexNormal;
    opts += OpenMesh::IO::Options::FaceNormal;
    opts += OpenMesh::IO::Options::VertexTexCoord;

    // 读取STL文件
    if (!OpenMesh::IO::read_mesh(meshRef, path, opts))
    {
        std::cout << "Error: Cannot read mesh file " << path << std::endl;
        return;
    }

    // 确保所有面都是三角形
    if (!meshRef.is_trimesh())
    {
        std::cout << "Mesh contains non-triangular faces, triangulating..." << std::endl;
        meshRef.triangulate();
    }

    meshRef.update_face_normals();
    meshRef.update_vertex_normals();

    AddMesh(mesh);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    auto s = event->size();
    ui->occWidget->resize(s.width(), s.height());
}

void MainWindow::initializeChildren()
{
    ui->occWidget->InitializeWindow();
}

void MainWindow::openFile()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getOpenFileName(
        this, "Choose Model", directoryPath,
        "All Files(*.*);;STEP (*.stp);;BRep (*.brep);;IGS (*.igs);;OBJ (*.obj);;STL (*.stl)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        if (filename.endsWith(".stp") || filename.endsWith(".step"))
            ReadSTP(filename.toStdString().c_str());
        else if (filename.endsWith(".brep"))
            ReadBRep(filename.toStdString().c_str());
        else if (filename.endsWith(".igs"))
            ReadIGS(filename.toStdString().c_str());
        else if (filename.endsWith(".obj") || filename.endsWith(".stl"))
            ReadMesh(filename.toStdString().c_str());

        // 刷新交互
        ui->occWidget->UpdateInteractiveContext();
    }

    // 存在物体则适应物体
    ui->occWidget->FitAllShapes();
}

void MainWindow::openCloud()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getOpenFileName(this, "Choose Model", directoryPath, "BRep (*.brep)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        if (filename.endsWith(".brep"))
            ReadCloud(filename.toStdString().c_str());

        // 刷新交互
        ui->occWidget->UpdateInteractiveContext();
    }

    // 存在物体则适应物体
    ui->occWidget->FitAllShapes();
}

void MainWindow::openDirectory()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString dirname = QFileDialog::getExistingDirectory(this, "Choose Directory", directoryPath,
                                                        QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    if (dirname != "")
    {
        QDir dir(dirname);

        // 获取目录下的所有文件名（不包括子目录中的文件）
        QStringList fileList = dir.entryList(QDir::Files);
        for (const QString &filename : fileList)
        {
            if (filename.endsWith(".brep"))
                ReadBRep((dirname + "/" + filename).toStdString().c_str());
            else if (filename.endsWith(".stp") || filename.endsWith(".step"))
                ReadSTP((dirname + "/" + filename).toStdString().c_str());
            else if (filename.endsWith(".igs"))
                ReadIGS((dirname + "/" + filename).toStdString().c_str());
        }

        // 刷新交互
        ui->occWidget->UpdateInteractiveContext();
    }

    // 存在物体则适应物体
    ui->occWidget->FitAllShapes();
}

void MainWindow::saveFile()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getSaveFileName(this, "Save File", directoryPath,
                                                    "STEP (*.stp);;BRep (*.brep);;IGS (*.igs);;OBJ (*.obj)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        // 获取路径，添加后缀
        std::string name = filename.toStdString();

        if (filename.endsWith(".stp") || filename.endsWith(".step"))
            WriteSTP(filename.toStdString().c_str());
        else if (filename.endsWith(".brep"))
            WriteBRep(filename.toStdString().c_str());
        else if (filename.endsWith(".igs"))
            WriteIGS(filename.toStdString().c_str());

        QMessageBox::information(this, "Tip", "Saved.");
    }
}

void MainWindow::saveCloud()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getSaveFileName(this, "Save File", directoryPath, "BRep (*.brep)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        // 获取路径，添加后缀
        std::string name = filename.toStdString();
        if (filename.endsWith(".brep"))
            WriteCloud(filename.toStdString().c_str());

        QMessageBox::information(this, "Tip", "Saved.");
    }
}

void MainWindow::saveSpecific()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getSaveFileName(this, "Save File", directoryPath, "STEP (*.stp)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        // 获取路径，添加后缀
        std::string name = filename.toStdString();

        if (filename.endsWith(".stp") || filename.endsWith(".step"))
        {
            // 创建写入器
            STEPControl_Writer writer;

            // 翻译所有形状
            auto shapes = ui->occWidget->SelectedShapes();
            for (auto sh : shapes)
                writer.Transfer(sh, STEPControl_AsIs);

            // 写入文件
            writer.Write(Standard_CString(filename.toStdString().c_str()));
        }

        QMessageBox::information(this, "Tip", "Saved.");
    }
}

void MainWindow::deleteFile()
{
    m_shapes.clear();
    m_selected.clear();
    m_clouds.clear();
    m_meshes.clear();

    // 刷新窗口
    ui->occWidget->ClearAll();
    ui->occWidget->UpdateInteractiveContext();
}

void MainWindow::smoothShade()
{
    ui->occWidget->SetShadeMode(OCCWidget::ShadeMode::Smooth);
    ui->actionFlat->setChecked(false);
    ui->actionSmooth->setChecked(true);

    ui->occWidget->UpdateInteractiveContext();
}

void MainWindow::flatShade()
{
    ui->occWidget->SetShadeMode(OCCWidget::ShadeMode::Flat);
    ui->actionFlat->setChecked(true);
    ui->actionSmooth->setChecked(false);

    ui->occWidget->UpdateInteractiveContext();
}

void MainWindow::meshFitting()
{
    auto cornerNodes = ui->occWidget->CornerNodes();
    auto boundaryNodes = ui->occWidget->BoundaryNodes();
    auto mesh = ui->occWidget->SelectedMesh();
    if (mesh)
    {
        MeshFit meshFit(mesh);
        auto face = meshFit.Build(cornerNodes, boundaryNodes);
        if (!face.IsNull())
            AddShape(OCCUtils::WirteAndRead(face, "temp.step"));
        // AddShape(face);
    }

    ui->occWidget->UpdateInteractiveContext();
}

void MainWindow::covering()
{
    std::vector<TopoDS_Shape> shapes;
    for (auto shape : m_shapes)
        shapes.push_back(shape);

    HalfMesh mesh(shapes);
    mesh.BuildTopo();
    mesh.BuildCoons();
    mesh.BuildGeom();
    mesh.BuildFilling();

    m_shapes.clear();
    for (auto face : mesh.BuildResults())
        AddShape(face);

    // 刷新窗口
    ui->occWidget->UpdateInteractiveContext();
}

void MainWindow::filling()
{
    TopoDS_Compound aCompound;
    TopoDS_Builder aBuilder;
    aBuilder.MakeCompound(aCompound);

    for (auto shape : m_shapes)
        aBuilder.Add(aCompound, shape);

    auto mapList = OCCUtils::GetAdjFaces(ui->occWidget->SelectedEdges(), aCompound);
    FillingBuilder filling(mapList);
    FillingParamData paramData;
    auto fillingFace = filling.Build(paramData);
    auto face = OCCUtils::WirteAndRead(fillingFace, "test.step");

    m_shapes.clear();
    AddShape(face);
    for (auto &&[_, f] : mapList)
        AddShape(f);
    // AddPointCloud(filling.cloud);

    // 刷新窗口
    ui->occWidget->UpdateInteractiveContext();
}

void MainWindow::blending()
{
    auto edges = ui->occWidget->SelectedEdges();
    auto vertices = ui->occWidget->SelectedVertices();
    qDebug() << "edge: " << edges.size();
    qDebug() << "vertices: " << vertices.size();
    if (edges.size() >= 2 && vertices.size() >= 2)
    {
        gp_Pnt P0, P1;
        gp_Vec V0, V1;
        int index = edges.size() - 2;
        auto curve0 = OCCUtils::FindVertex(edges[index], vertices[index], P0, V0);
        auto curve1 = OCCUtils::FindVertex(edges[index + 1], vertices[index + 1], P1, V1);
        auto curve = OCCUtils::Interpolate(P0, P1, V0, -V1);
        TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(curve);
        AddShape(edge);

        // 刷新窗口
        ui->occWidget->UpdateInteractiveContext();
    }
}